using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using SlimDX;
using SlimDX.Direct3D9;

using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace Splino.D3D
{
    /// <summary>
    /// Enumerator for switching between types of Splino Objects
    /// </summary>
    [Serializable()]
    public enum SplinoObjectType
    {
        /// <summary>Unknown Splino Object type</summary>
        Other,
        /// <summary>Default Splino Object type</summary>
        Object,
        /// <summary>Point Objects</summary>
        Point,
        /// <summary>Spline Objects</summary>
        Spline,
        /// <summary>System created objects</summary>
        System
    }

    /// <summary>
    /// An enumerator that describes what z depth layer to 
    /// draw a Splino Object in.
    /// </summary>
    public enum DrawLayer
    {
        /// <summary>Draws Splino Objects below all other scene objects</summary>
        BottomMost,
        /// <summary>Draws Splino Objects using normal Z-Buffering</summary>
        Scene,
        /// <summary>Draws Splino Objects above all other scene objects</summary>
        TopMost
    }

    /// <summary>
    /// An enumerator describing the types of object snaps
    /// </summary>
    public enum ObjectSnapType
    {
        /// <summary>Snap to the first point of a spline, used to close splines during spline drawing</summary>
        CloseSpline,
        /// <summary>Endpoint snap</summary>
        End,
        /// <summary>Midpoint snap</summary>
        Mid,
        /// <summary>Snap to nearest point on object</summary>
        Near,
        /// <summary>Snap to point object</summary>
        Point,
        /// <summary>Snap to center of object</summary>
        Center,
        /// <summary>Snap to object quadrant</summary>
        Quadrant,
        /// <summary>Snap to object intersection</summary>
        Intersect,
        /// <summary>Snap to perpendicular object intersection</summary>
        Perp,
        /// <summary>Snap to tangent object intersection</summary>
        Tan,
        /// <summary>No object snap was found</summary>
        None = 500,
    }

    /// <summary>
    /// A structure for holding a object snap type and location.
    /// </summary>
    public struct ObjectSnap
    {
        /// <summary>The enumerated type of snap</summary>
        public ObjectSnapType ObjectSnapType;
        /// <summary> The world space coordinates of the snap location</summary>
        public Vector3 SnapLocation;
    }

    /// <summary>
    /// The base class for all 3D scene objects that Splino
    /// can create
    /// </summary>
    [Serializable()]
    [XmlRoot("SplinoObject")]
    [XmlInclude(typeof(PointObject))]
    [XmlInclude(typeof(SplineObject))]
    [XmlInclude(typeof(LinearSplineObject))]
    [XmlInclude(typeof(NURBSObject))]
    [XmlInclude(typeof(WeightedPointObject))]
    public class SplinoObject : ISerializable
    {
        /// <summary>
        /// An array of verticies used by the Splino Object's
        /// vertex buffer
        /// </summary>
        protected CustomVertex.PositionColored[] _vertexList;
        /// <summary>
        /// The Direct3D vertex buffer that loads the object's
        /// verticies into the device's memory
        /// </summary>
        protected VertexBuffer _vertexBuffer;

        /// <summary>
        /// True if the Direct3D resources for this object have been
        /// disposed, false if they have not.
        /// </summary>
        protected bool _disposed = false;

        /// <summary>
        /// The full classname of the Splino Object
        /// </summary>
        protected String _format;
        /// <summary>
        /// Gets the full classname of the Splino Object
        /// </summary>
        public static String Format
        {
            get { return typeof(SplinoObject).FullName; }
        }

        /// <summary>
        /// Serialization Information used for copying and pasting
        /// Splino Objects
        /// </summary>
        protected SerializationInfo _serializationInfo;
        /// <summary>
        /// Streaming Context using for copying and pasting
        /// Splino Objects
        /// </summary>
        protected StreamingContext _streamingContext;

        /// <summary>
        /// A unique string representing the name
        /// of the object
        /// </summary>
        protected String _name = "";
        /// <summary>
        /// Gets or sets a unique string representing the name
        /// of the object
        /// </summary>
        public String Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// The non-unique part of an object's name
        /// that is appended a number to create a unique name
        /// </summary>
        protected String _baseName = "";
        /// <summary>
        /// Gets or sets the non-unique part of an object's name
        /// that is appended a number to create a unique name
        /// </summary>
        [XmlIgnore()]
        public String BaseName
        {
            get { return _baseName; }
            set { _baseName = value; }
        }

        /// <summary>
        /// The type of SplinoObject
        /// </summary>
        protected SplinoObjectType _type = new SplinoObjectType();
        /// <summary>
        /// Gets or sets the type of SplinoObject
        /// </summary>
        public SplinoObjectType Type
        {
            get { return _type; }
            set { _type = value; }
        }

        /// <summary>
        ///  Gets string representation of the object's type
        /// </summary>
        public String ObjectType
        {
            get { return GetType().Name.Replace("Object", ""); }
        }

        /// <summary>
        /// The transformation matrix applied to
        /// this SplinoObject
        /// </summary>
        protected Matrix _transformMatrix = Matrix.Identity;
        /// <summary>
        /// The transformation matrix applied to
        /// this SplinoObject
        /// </summary>
        [XmlIgnore()]
        public Matrix TransformMatrix
        {
            get { return _transformMatrix; }
            set { _transformMatrix = value; }
        }

        /// <summary>
        /// The parent object of the SplinoObject, which is the 
        /// SceneManager's MasterSceneObject if the object is not
        /// part of another scene object
        /// </summary>
        protected SplinoObject _sceneParent;
        /// <summary>
        /// Gets or sets the Splino Object's parent object, which is the 
        /// SceneManager's MasterSceneObject if the object is not
        /// part of another scene object
        /// </summary>
        [XmlIgnore()]
        public SplinoObject SceneParent
        {
            get { return _sceneParent; }
            set
            {
                _lastSceneParent = _sceneParent;
                _sceneParent = value;
            }
        }

        /// <summary>
        /// The string name of the object's parent.
        /// </summary>
        protected String _sceneParentName = "";
        /// <summary>
        /// Gets or sets the string name of the object's parent.
        /// This property is serialized instead of the actual parent 
        /// Splino Object to prevent circular references.
        /// </summary>
        public String SceneParentName
        {
            get { return _sceneParentName; }
            set { _sceneParentName = value; }
        }

        /// <summary>
        /// The object's previous parent before the current one
        /// </summary>
        protected SplinoObject _lastSceneParent;
        /// <summary>
        /// Gets the object's previous parent before the current one
        /// </summary>
        [XmlIgnore()]
        public SplinoObject LastSceneParent
        {
            get { return _lastSceneParent; }
        }

        /// <summary>
        /// A list of Splino Objects representing the child scene objects
        /// of this object
        /// </summary>
        protected List<SplinoObject> _sceneChildren = new List<SplinoObject>();
        /// <summary>
        /// Gets a list of Splino objects representing the child scene objects
        /// </summary>
        [XmlArray("SceneChildren")]
        [XmlArrayItem("SplinoObject", typeof(SplinoObject))]
        public virtual List<SplinoObject> SceneChildren
        {
            get { return _sceneChildren; }
        }

        /// <summary>
        /// The color used to render the object while unlocked 
        /// and unselected, in wireframe mode
        /// </summary>
        protected Color _color;
        /// <summary>
        /// Gets or sets the color used to render the object while
        /// unlocked and unselected
        /// </summary>
        [XmlIgnore()]
        public Color Color
        {
            get { return _color; }
            set 
            { 
                _color = value;
                if (!_locked && !_hidden)
                    _currentColor = value;
                UpdateColor();
                SceneManager.Instance.ColorChangedSplinoObject(this);
            }
        }

        /// <summary>
        /// Gets or sets a string used to serialize the object's
        /// color to XML
        /// </summary>
        public string ColorXml
        {
            get { return ColorTranslator.ToHtml(_color); }
            set { _color = ColorTranslator.FromHtml(value); }
        }

        /// <summary>
        /// The color used to render a selected object
        /// </summary>
        protected Color _selectedColor;
        /// <summary>
        /// Gets or sets the color used to render a selected object
        /// </summary>
        [XmlIgnore()]
        public Color SelectedColor
        {
            get { return _selectedColor; }
            set { _selectedColor = value; }
        }

        /// <summary>
        /// Gets or sets a string color representation used to serialize
        /// the object's selected color to XML
        /// </summary>
        public string SelectedColorXml
        {
            get { return ColorTranslator.ToHtml(_selectedColor); }
            set { _selectedColor = ColorTranslator.FromHtml(value); }
        }

        /// <summary>
        /// The color that is currently being used for the object
        /// whether it is in a selected, locked, or normal state
        /// </summary>
        protected Color _currentColor;
        /// <summary>
        /// Gets or sets the color that is currently being used for the object
        /// whether it is in a selected, locked, or normal state
        /// </summary>
        [XmlIgnore()]
        public Color CurrentColor
        {
            get { return _currentColor; }
            set { _currentColor = value; }
        }

        /// <summary>
        /// Gets or sets a string color representation used to 
        /// serialize an object's current color to XML
        /// </summary>
        public string CurrentColorXml
        {
            get { return ColorTranslator.ToHtml(_currentColor); }
            set { _currentColor = ColorTranslator.FromHtml(value); }
        }

        /// <summary>
        /// The index of the image to use from the SceneManagerTree's imageList
        /// for displaying the TreeNode icon in the TreeView.
        /// </summary>
        protected int _treeNodeImageIndex = 1000;
        /// <summary>
        /// The key of the image to use from the SceneManagerTree's imageList
        /// for displaying the TreeNode icon in the TreeView.
        /// </summary>
        protected string _treeNodeImageKey;
        /// <summary>
        /// Gets the keo of the image to use from the SceneManagerTree's imageList
        /// for displaying the TreeNode icon in the TreeView.
        /// </summary>
        public string TreeNodeImageKey { get { return _treeNodeImageKey; } }
        /// <summary>
        /// True if the form containing the object's properties is visible
        /// </summary>
        protected bool _propertiesWindowActive = false;
        /// <summary>
        /// Gets or sets if the form containing the object's properties is visible
        /// </summary>
        [XmlIgnore()]
        public bool PropertiesWindowActive
        {
            get { return _propertiesWindowActive; }
            set { _propertiesWindowActive = value; }
        }
        /// <summary>
        /// The form containing the object's properties
        /// </summary>
        protected SceneManagerTree _propertiesWindow;
        /// <summary>
        /// Gets or sets the form containing the object's properties
        /// </summary>
        [XmlIgnore()]
        public SceneManagerTree PropertiesWindow
        {
            get { return _propertiesWindow; }
            set { _propertiesWindow = value; }
        }

        /// <summary>
        /// True if the object is actively selected
        /// </summary>
        protected bool _selected;
        /// <summary>
        /// Gets if the object is actively selected
        /// </summary>
        [XmlIgnore()]
        public bool Selected { get { return _selected; } }

        /// <summary>
        /// Constructor
        /// </summary>
        public SplinoObject()
        {
            _type = SplinoObjectType.System;
            _color = Splino.Palette.splino07;
            _selectedColor = Splino.Palette.splino15;
            _currentColor = _color;
            _sceneChildren = new List<SplinoObject>();
        }

        /// <summary>
        /// The drawing layer that this Splino Object will be drawn on.
        /// </summary>
        [XmlIgnore()]
        protected DrawLayer _drawLayer = DrawLayer.Scene;
        /// <summary>
        /// Gets the drawing layer that this Splino Object will be drawn on.
        /// </summary>
        public DrawLayer DrawLayer { get { return _drawLayer; } }

        /// <summary>
        /// Set to true to prevent picking of object and ghosting its display
        /// </summary>
        protected bool _locked = false;
        /// <summary>
        /// Gets if the object is locked, preventing it from being selected
        /// and ghosting its display
        /// </summary>
        public bool Locked
        {
            get { return _locked; }
        }

        /// <summary>
        /// Set to true to hide the object, preventing it from being selected
        /// or rendered but persisting it in the scene
        /// </summary>
        protected bool _hidden = false;
        /// <summary>
        /// Gets whether the object is hidden, preventing it from being
        /// selected or rendered bu persisting it in the scene
        /// </summary>
        public bool Hidden
        {
            get { return _hidden; }
        }

        /// <summary>
        /// The number of verticies contained by this object
        /// </summary>
        protected int _vertexCount = 0;

        /// <summary>
        /// A list of points the mouse is currently snapped to on the object
        /// </summary>
        protected List<ObjectSnap> _activeObjectSnaps = new List<ObjectSnap>();
        /// <summary>
        /// Gets a list of points the mouse is currently snapped to on the object
        /// </summary>
        [XmlIgnore()]
        public List<ObjectSnap> ActiveObjectSnaps { get { return _activeObjectSnaps; } }

        /// <summary>
        /// Protected constructor for serializing a Splino Object during
        /// clipboard copy and paste operations
        /// </summary>
        /// <param name="info">Clipboard Serialization Info</param>
        /// <param name="context">Clipboard Streaming Context</param>
        protected SplinoObject(SerializationInfo info, StreamingContext context)
        {
            _name = info.GetString("Name");
            _baseName = info.GetString("BaseName");
            _type = (SplinoObjectType)info.GetValue("Type", typeof(SplinoObjectType));
            _sceneChildren = (List<SplinoObject>)info.GetValue("Children", typeof(List<SplinoObject>));
            _color = (Color)info.GetValue("Color", typeof(Color));
            _selectedColor = (Color)info.GetValue("SelectedColor", typeof(Color));
            _currentColor = (Color)info.GetValue("CurrentColor", typeof(Color));
            _sceneParentName = (String)info.GetValue("SceneParentName", typeof(String));
            _vertexCount = (int)info.GetValue("VertexCount", typeof(int));
            if (_vertexCount > 0)
            {
                _vertexList = new CustomVertex.PositionColored[_vertexCount];
                ResetBuffers();
            }
        }

        /// <summary>
        /// Used to serialize a Splino Object and place it in the clipboard
        /// </summary>
        /// <param name="serializationInfo">Clipoard Serialization Info</param>
        /// <param name="streamingContext">Clipboard Streaming Context</param>
        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        public virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
        {
            _serializationInfo = serializationInfo;
            _streamingContext = streamingContext;

            _serializationInfo.AddValue("Name", _name);
            _serializationInfo.AddValue("BaseName", _baseName);
            _serializationInfo.AddValue("Type", _type, typeof(SplinoObjectType));
            _serializationInfo.AddValue("Children", _sceneChildren, typeof(List<SplinoObject>));
            _serializationInfo.AddValue("Color", _color, typeof(Color));
            _serializationInfo.AddValue("SelectedColor", _selectedColor, typeof(Color));
            _serializationInfo.AddValue("CurrentColor", _currentColor, typeof(Color));
            _serializationInfo.AddValue("SceneParentName", _sceneParentName, typeof(String));
            _serializationInfo.AddValue("VertexCount", _vertexCount, typeof(int));
        }

        /// <summary>
        /// Displays the Splino Object's properties in a new form
        /// </summary>
        public virtual void ShowPropertiesWindow()
        {
            if (!_propertiesWindowActive)
            {
                _propertiesWindowActive = true;
                PropertiesWindow = new SceneManagerTree();
                PropertiesWindow.Show(SplinoForm.Instance);
            }
        }

        /// <summary>
        /// Closes the form display the Splino Object's properties
        /// </summary>
        public virtual void ClosePropertiesWindow()
        {
            _propertiesWindow.Close();
        }

        /// <summary>
        /// Creates a unique name for the Splino Object, generates a ListViewItem
        /// based off of its properties for display in the parent object's children
        /// list, and associates the object with its parent
        /// </summary>
        /// <param name="parentObject">The parent Splino Object</param>
        public virtual void Initialize(SplinoObject parentObject)
        {
            String parentName = parentObject.Name;
            bool definesParent;

            if (_sceneParent != null && _sceneParent.Type != SplinoObjectType.System)
                definesParent = true;
            else
                definesParent = false;

            // We remove the child object from its parent object to make sure
            // that the child object doesn't see its own name as a duplicate
            // name when checking the SceneManager hierarchy for duplicate names.
            parentObject.RemoveSceneChild(this);

            List<SplinoObject> childObjects = new List<SplinoObject>();
            foreach (SplinoObject splinoObject in _sceneChildren)
                childObjects.Add(splinoObject);

            if (_type != SplinoObjectType.System)
            {
                if (_name == "")
                {
                    String objectType;
                    if (definesParent)
                        objectType = parentName + "_" + GetType().Name;
                    else
                        objectType = GetType().Name;

                    objectType = objectType.Replace("Object", "");

                    if (SceneManager.Instance.ObjectCount.Contains(objectType))
                    {
                        int typeCount = (int)SceneManager.Instance.ObjectCount[objectType];
                        SceneManager.Instance.ObjectCount[objectType] = ++typeCount;
                    }
                    else
                        SceneManager.Instance.ObjectCount.Add(objectType, 1);

                    _name = objectType + SceneManager.Instance.ObjectCount[objectType];

                    //if (_sceneParent.Typesdfasdf
                    _baseName = _name;
                }
                else if (_baseName == "")
                    _baseName = _name;

                SceneManager.Instance.FixDuplicateName(this);
            }

            // Add the child object back to its parents once we are done with naming.
            parentObject.AddSceneChild(this);

            SceneManager.Instance.InitializedSplinoObject(this);

            foreach (SplinoObject splinoObject in childObjects)
                splinoObject.Initialize(this);
        }

        /// <summary>
        /// Reinitializes the object's vertex buffer and sets
        /// the vertex array as data to the buffer.  Recursively
        /// calls ResetBuffers for the object's children.
        /// </summary>
        public virtual void ResetBuffers()
        {
            if (_disposed)
            {
                foreach (SplinoObject splinoObject in _sceneChildren)
                    if (splinoObject != null)
                        splinoObject.ResetBuffers();

                if (_vertexCount > 0)
                {
                    if (_vertexBuffer == null || _vertexBuffer.Disposed)
                    {
                        _vertexBuffer = new VertexBuffer(
                            DeviceManager.Instance.Device, 
                            CustomVertex.PositionColored.SizeInBytes*_vertexList.Length, 
                            Usage.WriteOnly, 
                            CustomVertex.PositionColored.Format, 
                            Pool.Default);
                    }

                    DataStream stream = _vertexBuffer.Lock(0, _vertexList.Length * CustomVertex.PositionColored.SizeInBytes, LockFlags.None);
                    stream.WriteRange(_vertexList);
                    _vertexBuffer.Unlock();
                }

                _disposed = false;
            }
        }

        /// <summary>
        /// Disposes the object's vertex buffer, freeing its memory from the
        /// rendering device.  Recursively calls Dispose() for all child objects
        /// </summary>
        public virtual void Dispose()
        {
            if (!_disposed)
            {
                foreach (SplinoObject splinoObject in _sceneChildren)
                    splinoObject.Dispose();

                if (_vertexBuffer != null)
                    _vertexBuffer.Dispose();

                _disposed = true;
            }
        }

        /// <summary>
        /// Renders the Splino Object onscreen.  Recursively calls Draw()
        /// for all child objects so that they are drawn first.
        /// </summary>
        /// <param name="drawLayer">The drawing layer that the SceneManager
        /// is currently rendering.</param>
        /// <param name="splinoViewport">The viewport being drawn to</param>
        public virtual void Draw(DrawLayer drawLayer, SplinoViewport splinoViewport)
        {
            foreach (SplinoObject splinoObject in _sceneChildren)
                if (!splinoObject._hidden)
                    splinoObject.Draw(drawLayer, splinoViewport);
            DeviceManager.Instance.Device.SetTransform(TransformState.World, _transformMatrix);
        }

        /// <summary>
        /// Changes the color of each vertex in the vertex list
        /// resets the data in the vertex buffer.
        /// </summary>
        public virtual void UpdateColor()
        {
            for (int i=0; i<_vertexCount; i++)
                _vertexList[i].Color = _currentColor.ToArgb();

            if (_vertexList != null)
            {
                DataStream stream = _vertexBuffer.Lock(0, 0, LockFlags.None);
                stream.WriteRange(_vertexList);
                _vertexBuffer.Unlock();
            }
            DeviceManager.Instance.RequestRender();
        }

        /// <summary>
        /// Copies this Splino Object to a new Splino Object.
        /// </summary>
        /// <returns>A copy of the Splino Object</returns>
        public virtual SplinoObject Copy()
        {
            return new SplinoObject();
        }

        /// <summary>
        /// Transfers the transformation matrix to the object, and resets
        /// the transformation matrix back to the identity matrix
        /// </summary>
        public virtual void FinalizeTransform()
        {
            foreach (SplinoObject child in SceneChildren)
                child.FinalizeTransform();

            _transformMatrix = Matrix.Identity;
        }

        /// <summary>
        /// Sets the transformation matrix back to the identity for the objectc
        /// and recursively for all of its children
        /// </summary>
        public virtual void ResetTransform()
        {
            foreach (SplinoObject child in SceneChildren)
                child.ResetTransform();

            _transformMatrix = Matrix.Identity;
        }

        /// <summary>
        /// Applies a transformation matrix to the Splino Object
        /// </summary>
        /// <param name="transform">Transformation matrix</param>
        public virtual void Transform(Matrix transform)
        {
            foreach (SplinoObject child in SceneChildren)
                child.Transform(transform);

            if (Transformed != null)
                Transformed(this, new EventArgs());

            _transformMatrix *= transform;
            DeviceManager.Instance.RequestRender();
        }

        public virtual void Move(Vector3 translation)
        {
            Translate(translation);

            if (Moved != null)
                Moved(this, new EventArgs());
        }

        /// <summary>
        /// Moves the object by the given vector
        /// </summary>
        /// <param name="translation">The amount to translate</param>
        public virtual void Translate(Vector3 translation)
        {
            Transform(Matrix.Translation(translation));

            if (Translated != null)
                Translated(this, new EventArgs());
        }

        /// <summary>
        /// Scales the object by the given vector
        /// </summary>
        /// <param name="scale">The amount to scale</param>
        public virtual void Scale(Vector3 scale)
        {
            Transform(Matrix.Scaling(scale));
        }

        /// <summary>
        /// Rotates the object by the given quaternion
        /// </summary>
        /// <param name="quat">The axis and amount to rotate</param>
        public virtual void Rotate(Quaternion quat)
        {
            Transform(Matrix.RotationQuaternion(quat));
        }

        /// <summary>
        /// Rotates the object along the given axis and by the given radians
        /// </summary>
        /// <param name="axis">The axis to rotate along</param>
        /// <param name="radians">The amount to rotate by in radians</param>
        public virtual void Rotate(Vector3 axis, float radians)
        {
            Transform(Matrix.RotationAxis(axis, radians));
        }

        /// <summary>
        /// Adds a Splino Object as a child object to this object.
        /// Making this object responsible for its drawing, 
        /// transformation, and other calls.  Removes the current
        /// child's parent if one exists.
        /// </summary>
        /// <param name="child">The child Splino Object</param>
        public virtual void AddSceneChild(SplinoObject child)
        {
            if (child.SceneParent != null)
                child.SceneParent.RemoveSceneChild(child);

            _sceneChildren.Add(child);
            child.SceneParent = this;
            child.SceneParentName = this.Name;
        }

        /// <summary>
        /// Removes a child Splino Object from this object's list of
        /// children, if it is present in the list, and nullifies
        /// the child object's parent.
        /// </summary>
        /// <param name="child">The child object to remove</param>
        public virtual void RemoveSceneChild(SplinoObject child)
        {
            _sceneChildren.Remove(child);
            child.SceneParent = null;
            child.SceneParentName = "";
        }

        /// <summary>
        /// Prevents calls to Draw(), making the object invisible
        /// and unselectable
        /// </summary>
        public virtual void Hide()
        {
            _hidden = true;

            SceneManager.Instance.HiddenSplinoObject(this);
            DeviceManager.Instance.RequestRender();
        }

        /// <summary>
        /// Allows calls to Draw(), making the object visible
        /// and selectable if it isn't locked.
        /// </summary>
        public virtual void Show()
        {
            _hidden = false;

            SceneManager.Instance.ShownSplinoObject(this);
            DeviceManager.Instance.RequestRender();
        }

        /// <summary>
        /// Makes the object unselectable and ghosts its display
        /// </summary>
        public virtual void Lock()
        {
            _locked = true;
            _currentColor = Palette.LockedObject;

            _drawLayer = DrawLayer.BottomMost;

            UpdateColor();

            SceneManager.Instance.LockedSplinoObject(this);
            DeviceManager.Instance.RequestRender();
        }

        /// <summary>
        /// Makes the object selectable and returns the object's
        /// normal drawing color
        /// </summary>
        public virtual void Unlock()
        {
            _locked = false;
            _currentColor = _color;

            _drawLayer = DrawLayer.Scene;

            UpdateColor();

            SceneManager.Instance.UnlockedSplinoObject(this);
            DeviceManager.Instance.RequestRender();
        }

        /// <summary>
        /// Removes the object from its parent object's child list,
        /// thus removing it from the scene hierarchy, and disposes
        /// the vertex buffer, freeing its device memory.
        /// </summary>
        public virtual void Delete()
        {
            if (_sceneParent != null)
                _sceneParent.RemoveSceneChild(this);
            if (_selected)
                Deselect();
            Dispose();

            DeviceManager.Instance.RequestRender();
        }

        /// <summary>
        /// Reattaches the object to its last scene parent, returning
        /// the object to the scene hierarchy, and resets the object's
        /// buffers.
        /// </summary>
        public virtual void Undelete()
        {
            ResetBuffers();
            _lastSceneParent.AddSceneChild(this);

            DeviceManager.Instance.RequestRender();
        }

        /// <summary>
        /// Shoots a ray from a starting position vector and a unit vector
        /// direction, and tests to see if it intersects with this Splino Object,
        /// returning true if it does intersect and false if it does not.  Used
        /// for picking/selecting objects.
        /// </summary>
        /// <param name="viewport">The viewport that contains the picking ray and 
        /// camera used for the picking test</param>
        /// <param name="rayPosition">The start position of the picking ray, generally
        /// the camera's location subtracted from an unprojected mouse click</param>
        /// <param name="rayDirection">A unit vector </param>
        /// <returns>True if the picking ray intersected the object, and false
        /// if it did not</returns>
        public virtual List<SplinoObject> IntersectRay(SplinoViewport viewport, Vector3 rayPosition, Vector3 rayDirection)
        {
            List<SplinoObject> splinoObjects = new List<SplinoObject>();

            if (!_hidden)
                foreach (SplinoObject splinoObject in _sceneChildren)
                    if (!splinoObject.Hidden)
                        splinoObjects.AddRange(splinoObject.IntersectRay(viewport, rayPosition, rayDirection));

            return splinoObjects;
        }

        /// <summary>
        /// Tests the object to see if any part of it is clipped by the selection
        /// rectangle in a given Splino Viewport, returning true if the object
        /// was clipped by the rectangle (or completely contained by), and false
        /// if it is not.  Used for multiple object picking/selection
        /// </summary>
        /// <param name="splinoViewport">The viewport containing the selection
        /// rectangle</param>
        /// <returns>True if the object was clipped by the selection rectangle,
        /// false if it was not</returns>
        public virtual List<SplinoObject> IntersectInclusive(SplinoViewport splinoViewport)
        {
            List<SplinoObject> splinoObjects = new List<SplinoObject>();

            if (!_hidden)
                foreach (SplinoObject splinoObject in _sceneChildren)
                    if (!splinoObject.Hidden)
                        splinoObjects.AddRange(splinoObject.IntersectInclusive(splinoViewport));

            return splinoObjects;
        }

        /// <summary>
        /// Tests the object to see if it is completely contained by the
        /// selection rectangle in a given Splino Viewport, returning true
        /// if it is, and false if it is not.  Used for multiple object
        /// selection/picking
        /// </summary>
        /// <param name="splinoViewport">The Splino Viewport containg
        /// the selection rectangle</param>
        /// <returns>True if this object is completely contained by
        /// the Viewport's selection rectangle, false if it is not</returns>
        public virtual List<SplinoObject> IntersectExclusive(SplinoViewport splinoViewport)
        {
            List<SplinoObject> splinoObjects = new List<SplinoObject>();

            if (!_hidden && !_locked)
                foreach (SplinoObject splinoObject in _sceneChildren)
                    if (!splinoObject.Hidden)
                        splinoObjects.AddRange(splinoObject.IntersectExclusive(splinoViewport));

            return splinoObjects;
        }

        /// <summary>
        /// Shoots a ray from the drawing point through the scene, testing for intersections
        /// with an object and its children, and returns a list of snap type and location pairs, if
        /// any exist.
        /// </summary>
        /// <param name="splinoViewport">The viewport to shoot the ray through</param>
        /// <param name="rayPosition">The start of the ray</param>
        /// <param name="rayDirection">The ray's direction</param>
        /// <returns>A list of snap type and location pairs</returns>
        public virtual List<ObjectSnap> SnapTest(SplinoViewport splinoViewport, Vector3 rayPosition, Vector3 rayDirection)
        {
            List<ObjectSnap> objectSnaps = new List<ObjectSnap>();

            if (!_hidden)
                foreach (SplinoObject splinoObject in _sceneChildren)
                    if (!splinoObject.Hidden)
                        objectSnaps.AddRange(splinoObject.SnapTest(splinoViewport, rayPosition, rayDirection));

            _activeObjectSnaps = objectSnaps;
            return objectSnaps;
        }

        /// <summary>
        /// Adds the object to the Scene Manager's list of selected objects
        /// and change's the object's color to the selected state color
        /// </summary>
        public virtual void Select()
        {
            if (!_locked && !_hidden)
            {
                foreach (SplinoObject splinoObject in SceneChildren)
                    if (!splinoObject.Locked && !splinoObject.Hidden)
                        splinoObject.Select();

                _currentColor = _selectedColor;

                if (!SceneManager.Instance.SelectedObjects.Contains(this))
                    SceneManager.Instance.SelectedObjects.Add(this);

                _selected = true;
                SplinoForm.Instance.CutMenuItem.Enabled = true;
                SplinoForm.Instance.CopyMenuItem.Enabled = true;
                SplinoForm.Instance.DeleteMenuItem.Enabled = true;

                _drawLayer = DrawLayer.TopMost;

                UpdateColor();

                SceneManager.Instance.SelectedSplinoObject(this);
                DeviceManager.Instance.RequestRender();
            }
        }

        /// <summary>
        /// Removes the object from the Scene Manager's list of selected
        /// objects and returns the objects color to its normal state
        /// </summary>
        public virtual void Deselect()
        {
            foreach (SplinoObject splinoObject in SceneChildren)
                splinoObject.Deselect();

            if (!_locked)
                _currentColor = _color;

            if (SceneManager.Instance.SelectedObjects.Contains(this))
                SceneManager.Instance.SelectedObjects.Remove(this);

            _selected = false;

            if (SceneManager.Instance.SelectedObjects.Count == 0)
            {
                SplinoForm.Instance.CutMenuItem.Enabled = false;
                SplinoForm.Instance.CopyMenuItem.Enabled = false;
                SplinoForm.Instance.DeleteMenuItem.Enabled = false;
            }

            _drawLayer = DrawLayer.Scene;

            UpdateColor();

            SceneManager.Instance.DeselectedSplinoObject(this);
            DeviceManager.Instance.RequestRender();
        }

        /// <summary>
        /// Event fired when a Splino object is transformed
        /// </summary>
        public event EventHandler Transformed;
        /// <summary>Event fired when a Splino object is moved.</summary>
        public event EventHandler Translated;
        public event EventHandler Moved;
    }
}